package org.offsync.rich
import java.lang.annotation.AnnotationFormatError
import java.awt.AWTError
import java.nio.charset.CoderMalfunctionError
import javax.xml.parsers.FactoryConfigurationError
import java.io.IOError
import java.util.ServiceConfigurationError
import javax.xml.transform.TransformerFactoryConfigurationError
import java.util.concurrent.atomic.AtomicReference

case class RichThrowable[T <: Throwable](throwable: T) {
  def captureStackTrace: T = {
    throwable.fillInStackTrace()
    return throwable
  }
  
  def wrap(exceptionClass: Class[_ <: Throwable]): Throwable = {
    val wrapMap = RichThrowable.wrapMapRef.get()
    
    wrapMap.get(exceptionClass) match {
      case Some(builder) => builder(throwable)
      case _ => {
        try {
          val constructor = exceptionClass.getConstructor(classOf[String], classOf[Throwable])
          val builder = { t: Throwable =>
            constructor.newInstance(t.getMessage(), t)
          }
          RichThrowable.wrapMapRef.set(wrapMap + (exceptionClass -> builder))
          return builder(throwable)
        } catch {
          case _ => 
        }
        
        try {
          val constructor = exceptionClass.getConstructor(classOf[String])
          val builder = { t: Throwable =>
            val newException = constructor.newInstance(t.getMessage())
            newException.initCause(t)
            newException
          }
          RichThrowable.wrapMapRef.set(wrapMap + (exceptionClass -> builder))
          return builder(throwable)
        } catch {
          case _ => 
        }
        
        try {
          val constructor = exceptionClass.getConstructor(classOf[Throwable])
          val builder = { t: Throwable =>
            constructor.newInstance(t)
          }
          RichThrowable.wrapMapRef.set(wrapMap + (exceptionClass -> builder))
          return builder(throwable)
        } catch {
          case _ => 
        }
        
        try {
          val constructor = exceptionClass.getConstructor()
          val builder = { t: Throwable =>
            val newException = constructor.newInstance()
            newException.initCause(t)
            newException
          }
          RichThrowable.wrapMapRef.set(wrapMap + (exceptionClass -> builder))
          return builder(throwable)
        } catch {
          case _ => 
        }
        
        throw new Throwable(throwable)
      }
    }
  }
  
  def wrap(): Throwable = {
    return wrap(throwable.getClass)
  }
}

object RichThrowable {
  val wrapMapRef = new AtomicReference[Map[Class[_], Throwable => Throwable]](
      Map[Class[_], Throwable => Throwable]())
}
